package cn.zwx.member.svc.rpc;

import cn.zwx.base.entity.ResponseEntity;
import cn.zwx.base.exception.BizWrapException;
import cn.zwx.base.utils.ValidationUtils;
import cn.zwx.member.svc.domain.bo.UserPointsBO;
import cn.zwx.member.svc.domain.constans.MemberConstans;
import cn.zwx.member.svc.domain.entity.MemberEntity;
import cn.zwx.member.svc.domain.enums.MemberCodeEnum;
import cn.zwx.member.svc.domain.enums.SignBackupEnum;
import cn.zwx.member.svc.domain.warp.ExceptionWrap;
import cn.zwx.member.svc.rpc.dto.UserSignCountDTO;
import cn.zwx.member.svc.rpc.dto.UserSignDTO;
import cn.zwx.member.svc.rpc.facade.MemberSignInRecordService;
import cn.zwx.member.svc.rpc.vo.UserSignCountVO;
import cn.zwx.member.svc.service.MemberService;
import com.alibaba.fastjson.JSON;
import org.apache.dubbo.apidocs.annotations.ApiDoc;
import org.apache.dubbo.apidocs.annotations.ApiModule;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author zhangwenxue
 * ApiModule 必要 api-doc会查找所有带有@ApiModule的bean对象
 */
@Service
@ApiModule(value = "用户签到相关接口", apiInterface = MemberSignInRecordService.class)
@DubboService(version = "v1.0.0",interfaceClass = MemberSignInRecordService.class,timeout = 8000)
public class MemberSignInRecordServiceImpl implements MemberSignInRecordService {

    private static final Logger logger = LoggerFactory.getLogger(MemberSignInRecordServiceImpl.class);
//    @Resource
//    private UserSignService userSignService;
//    @Resource
//    private SignRewardService signRewardService;
    @Resource
    private MemberService memberService;
//    @Resource
//    private UserPointsService userPointsService;
    @Resource
    private RedisTemplate<String,Object> redisTemplate;


    @Override
    @Transactional(rollbackFor = Exception.class)
    @ApiDoc(value = "用户签到接口")
    public ResponseEntity<Void> userSign(UserSignDTO dto) throws BizWrapException {
        // 1.验证参数
        ValidationUtils.getInstance().validate(dto);
        // 2.获取用户信息
        final MemberEntity memberEntity = memberService.selectMemberById(dto.getUserId());
        if (Objects.isNull(memberEntity)){
            throw ExceptionWrap.wrap(MemberCodeEnum.USER_INFO_NOT_EXIST_ERROR);
        }
        // 3.获取签到使用的
        final LocalDateTime localDateTime = LocalDateTime.now();
        final int offset = localDateTime.getDayOfMonth() - 1;
        final String redisKey = createSignKey(localDateTime,dto.getUserId());
        // 4.check是否已经签到
        final Boolean isSign  = redisTemplate.opsForValue().getBit(redisKey, offset);
        if (Boolean.TRUE.equals(isSign)){
            throw ExceptionWrap.wrap(MemberCodeEnum.ALREADY_SIGN_ERROR);
        }
        // 5.保存签到结果
//        final UserSignEntity userSignEntity = new UserSignEntity();
//        userSignEntity.setDate(new Date());
//        userSignEntity.setYear(localDateTime.getYear());
//        userSignEntity.setMoth(localDateTime.getMonthValue());
//        userSignEntity.setUserId(dto.getUserId());
//        userSignEntity.setIsBackup(SignBackupEnum.NORMAL.getCode());
//        userSignService.insert(userSignEntity);
        // 6. 写入redis setbit key offset 1
        final Boolean bitRes = redisTemplate.opsForValue().setBit(redisKey, offset, true);
        logger.info(String.format("%s签到成功，签到结果：%s", JSON.toJSONString(localDateTime.format(DateTimeFormatter.ofPattern("yyyyMM"))),
                JSON.toJSONString(bitRes)));
        // 6.签到成功新增积分奖励
        this.processSignReward(dto);
        return ResponseEntity.success();
    }

    public void processSignReward(UserSignDTO dto) throws BizWrapException {
        final UserSignCountDTO userSignCountDTO = new UserSignCountDTO();
        userSignCountDTO.setUserId(dto.getUserId());
        final ResponseEntity<UserSignCountVO> voResponseEntity = this.userSignCount(userSignCountDTO);
        final UserSignCountVO entityData = voResponseEntity.getData();
//        final SignRewardEntity signReward = signRewardService.getSignReward(entityData.getSignCount());
//        final UserPointsBO userPointsBO = new UserPointsBO();
//        userPointsBO.setUserId(dto.getUserId());
//        List<UserPointsBO.UserPointsDetailBO> detailList = new ArrayList<>();
//        final UserPointsBO.UserPointsDetailBO detailBO = new UserPointsBO.UserPointsDetailBO();
//        detailBO.setPointsValue(signReward.getNumber().longValue());
//        detailBO.setPointsTypeDesc("签到奖励");
//        detailBO.setPointsType(1);
//        detailList.add(detailBO);
//        userPointsBO.setDetailList(detailList);
//        userPointsService.addUserPoint(userPointsBO);
    }

    @Override
    @ApiDoc(value = "查询本月用户签到次数接口")
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity<UserSignCountVO> userSignCount(UserSignCountDTO dto) throws BizWrapException {
        // 1.验证参数
        ValidationUtils.getInstance().validate(dto);
        // 2.获取签到的key
        final LocalDateTime localDateTime = LocalDateTime.now();
        final String signKey = createSignKey(localDateTime,dto.getUserId());
        // 3.获取本月截至今天为止的所有的签到记录
        // bitfield user:sign:5:202105 u14 0 ，u 表示无符号 ，14 表示今天是14号，0 表示索引，即从第一天开始
        BitFieldSubCommands bitFieldSubCommands = BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(localDateTime.getDayOfMonth()))
                .valueAt(0);
        final List<Long> resultList  = redisTemplate.opsForValue().bitField(signKey, bitFieldSubCommands);
        // 4.返回参数处理
        final UserSignCountVO userSignCountVO = new UserSignCountVO();
        userSignCountVO.setSignCount(0);
        // 初始化总签到天数
        userSignCountVO.setTotalDay(0);
        // 初始化今天是否已签到
        userSignCountVO.setTodaySignIn(false);
        // 没有获取到签到记录直接返回
        if (CollectionUtils.isEmpty(resultList)){
            return ResponseEntity.success(userSignCountVO);
        }
        Long num  = resultList.get(0);
        if (ObjectUtils.isEmpty(num) || num.compareTo(MemberConstans.LONG_0) == 0){
            return ResponseEntity.success(userSignCountVO);
        }
        return ResponseEntity.success(calUserSignInfo(localDateTime, num));
    }

    private static UserSignCountVO calUserSignInfo(LocalDateTime localDateTime, Long num) {
        UserSignCountVO userSignCountVO = new UserSignCountVO();
        int count = 0;
        int totalDay = 0;
        boolean todaySignIn = false;
        //如果为0，签到结束
        while ((num & 1) != 0) {
            // 让这个数字与1 做与运算，得到数字的最后一个bit位 判断这个数字是否为0
            count++;
            num >>>= 1;
        }
        // 计算总签到天数
        for (int i = 0; i < localDateTime.getDayOfMonth(); i++) {
            if ((num & 1) != 0) {
                totalDay++;
            }
            num >>>= 1;
        }
        // 检查今天是否已签到
        if ((num & (1L << (localDateTime.getDayOfMonth() - 1))) != 0) {
            todaySignIn = true;
        }
        userSignCountVO.setSignCount(count);
        userSignCountVO.setTotalDay(totalDay);
        userSignCountVO.setTodaySignIn(todaySignIn);
        return userSignCountVO;
    }

    private static String createSignKey(LocalDateTime localDateTime,String userId) {
        int offset = localDateTime.getDayOfMonth() - 1;
        String redisKey = MemberConstans.USER_SIGN_KEY_PREFIX + userId + ":"+localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM"));
        logger.info(String.format("入参：key:%s，offset:%s", JSON.toJSONString(redisKey), JSON.toJSONString(offset)));
        return redisKey;
    }
}
